/**

This file is part of MaCI/GIMnet.

MaCI/GIMnet is free software: you can redistribute it and/or modify it 
under the terms of the GNU Lesser General Public License as published 
by the Free Software Foundation, either version 3 of the License, or 
(at your option) any later version.

MaCI/GIMnet is distributed in the hope that it will be useful, but WITHOUT 
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 
FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public 
License for more details.

You should have received a copy of the GNU Lesser General Public 
License along with GIMnet. (See COPYING.LESSER) If not, see 
<http://www.gnu.org/licenses/>.

**/
/**
 * \file
 * \brief SVG Image data decoder/encoder.
 * \author Antti Maula <antti.maula@tkk.fi>
 */

#ifndef _GIM_MOTTI_SVGIMAGE_HPP_
#define _GIM_MOTTI_SVGIMAGE_HPP_

#include "owndebug.h"
#include <string>
#include <vector>
#include <libxml/xmlmemory.h>
#include <libxml/parser.h>

//*****************************************************************************
//*****************************************************************************
//*****************************************************************************

enum ESVGObjectType { KLine, KRect, KEllipse };
//*****************************************************************************

/** SVG entry style line parameters
 *
 * style="fill: none; fill-opacity:0; stroke-width: 0.001; stroke: #000000"
 */
struct SSVGStyle
{
public:
  SSVGStyle() 
    :  gotFill(false),
       fillR(0),
       fillG(0),
       fillB(0),
       fill_opacity(0.0),
       stroke_width(0.001),
       strokeR(0),
       strokeG(0),
       strokeB(0) {}
  ~SSVGStyle() {}

  bool DecodeStyle(const std::string &aSVGStyle) {
    bool result = true;
    char data[2][128];
    const char *svg = aSVGStyle.c_str();
    const char *svgptr = svg;
    int findex = 0;
    int state = 0; // 0 == reading key, 1 == reading value, 2 == pair completed

    // Read input as long as end not reached
    bool parse = true;
    do {
      const char byte = *svgptr;
      switch(byte) {
      case ':':
        // End of key
        data[state][findex] = 0;
        state = 1;
        findex = 0;
        break;

      case '\0':
        parse = false;
      case ';':
        // End of value
        data[state][findex] = 0;
        state = 2;
        findex = 0;
        break;

      case ' ':
        // Do nada - no state change; no input pointer increment, Nada.
        break;

      default:
        data[state][findex] = byte;
        ++findex;
        break;
      }

      if (state == 2) {
        // Got pair; check contents
        const std::string key(data[0]);
        const std::string value(data[1]);
        result = DecodeKeyValuePair(key, value);
        if (!result) break;
        state = 0;
      }
      ++svgptr;
    } while(parse);
  
    if (!result) {
      dPrint(ODWARN,"Warn: Failed to decode Style line: '%s'", aSVGStyle.c_str());
    }
    return result;
  }

  int GetStrokeR(void) const { return strokeR; }
  int GetStrokeG(void) const { return strokeG; }
  int GetStrokeB(void) const { return strokeB; }

  bool GotFill(void) const { return gotFill; }
  int GetFillR(void) const { return fillR; }
  int GetFillG(void) const { return fillG; }
  int GetFillB(void) const { return fillB; }

  float GetStrokeWidth(void) const { return stroke_width; }
  float GetFillOpacity(void) const { return fill_opacity; }

private:
  bool DecodeKeyValuePair(const std::string &aKey, const std::string &aValue) {
    bool result = true;

    // Do crude check here; maybe split in future :)
    if (aKey == "fill") {
      // Get Fill as R,G,B or None.
      if (aValue != "none") {
        gotFill = true;
        unsigned int r,g,b;
        if (sscanf(aValue.c_str(),"#%2x%2x%2x", &r, &g, &b) != 3) {
          fillR = r;
          fillG = g;
          fillB = b;

        } else {
          result = false;

        }

      } else {
        gotFill = false;
        
      }
    } else if (aKey == "fill-opacity") {
      // Get Fill Opacity as float
      fill_opacity = atof(aValue.c_str());

    } else if (aKey == "stroke-width") {
      // Get Stroke-width as float.
      stroke_width = atof(aValue.c_str());
      
    } else if (aKey == "stroke") { 
      // Get stroke as R,G,B
      unsigned int r,g,b;
      if (sscanf(aValue.c_str(),"#%2x%2x%2x", &r, &g, &b) != 3) {
        result = false;

      } else {
        strokeR = r;
        strokeG = g;
        strokeB = b;
      }

    } else {
      // Unknown style flag - ignore.
      dPrint(ODTEST,"Unknown style key '%s'. Ignoring!", aKey.c_str());

    }

    // Return result.
    return result;
  }
public:
  
  // fill
  bool gotFill;
  unsigned char fillR;
  unsigned char fillG;
  unsigned char fillB;

  // Fill opacity
  float fill_opacity;

  // Store width
  float stroke_width;

  // Stroke
  unsigned char strokeR;
  unsigned char strokeG;
  unsigned char strokeB;
};
//*****************************************************************************

struct SSVGObject
{
  SSVGObject()
    : type(),
      style(),
      p() {}
  ~SSVGObject() {}

  void InitLine(const SSVGStyle &aStyle,
                const float x1,
                const float y1,
                const float x2,
                const float y2) {
    type = KLine;
    style = aStyle;
    p.line.x1 = x1;
    p.line.y1 = y1;
    p.line.x2 = x2;
    p.line.y2 = y2;
  }

  void InitRect(const SSVGStyle &aStyle,
                const float x,
                const float y,
                const float width,
                const float height,
                const float rx,
                const float ry) {
    type = KRect;
    style = aStyle;
    p.rect.x = x;
    p.rect.y = y;
    p.rect.width = width;
    p.rect.height = height;
    p.rect.rx = rx;
    p.rect.ry = ry;

  }

  void InitEllipse(const SSVGStyle &aStyle,
                   const float cx,
                   const float cy,
                   const float rx,
                   const float ry) {
    type = KEllipse;
    style = aStyle;
    p.ellipse.cx = cx;
    p.ellipse.cy = cy;
    p.ellipse.rx = rx;
    p.ellipse.ry = ry;
    
  }

public:
  ESVGObjectType type;
  SSVGStyle style;
  union {
    struct line {
      float x1;
      float y1;
      float x2;
      float y2;
    } line;
    struct rect {
      float x;
      float y;
      float width;
      float height;
      float rx;
      float ry;
    } rect;
    struct ellipse {
      float cx;
      float cy;
      float rx;
      float ry;
    } ellipse;
  } p;
};
//*****************************************************************************
typedef std::vector<SSVGObject> TSVGObjectArray;
//*****************************************************************************

class CSVGImage
{
public:
  CSVGImage(void);
  ~CSVGImage();

  bool DecodeSVGString(const std::string &aSVG);
  bool DecodeSVGFile(const std::string &aSVG);

  const TSVGObjectArray &GetObjectArray();


private: // Functions
  bool DecodeSVG(xmlDocPtr aDocp, xmlNodePtr aNodep);
  bool DecodeTagLine(xmlDocPtr docp, xmlNodePtr nodep);
  bool DecodeTagRect(xmlDocPtr docp, xmlNodePtr nodep);
  bool DecodeTagEllipse(xmlDocPtr docp, xmlNodePtr nodep);
  
  void AddObject(SSVGObject &aObject);

private: // Variables
  std::string iXMLData;
  TSVGObjectArray iObjectArray;
};
#endif
//*****************************************************************************
